package repository

import (
	"context"
	"database/sql"
	"fmt"
	"strings"

	"mall/common/modelx"
	"mall/service/backend/model"

	"github.com/volatiletech/sqlboiler/v4/boil"
	"github.com/volatiletech/sqlboiler/v4/queries/qm"
	"github.com/zeromicro/go-zero/core/stores/cache"
)

type (
	// BackendRoleRepository is an interface to be customized, add more methods here,
	// and implement the added methods in customBackendRoleRepository.
	BackendRoleRepository interface {
		Conn() error
		FindOne(ctx context.Context, id int64, cols []string) (*model.BackendRole, error)
		GetList(ctx context.Context, cols model.M, withCount int) ([]*model.BackendRole, int64, error)
		Insert(ctx context.Context, role *model.BackendRole) error
		Update(ctx context.Context, role *model.BackendRole) (int64, error)
		UpdateBatch(ctx context.Context, ids []int64, cols model.M) (int64, error)
		ForceDeleteBatch(ctx context.Context, ids []int64) (int64, error)
		SetMenus(ctx context.Context, role *model.BackendRole, menus []*model.BackendMenu) error
		SetPerms(ctx context.Context, role *model.BackendRole, perms []*model.BackendPermission) error
	}

	customBackendRoleRepository struct {
		cacheConf  cache.CacheConf
		dataSource string

		mysqlConn *sql.DB
	}
)

// NewBackendRoleRepository returns a repository for the database table.
func NewBackendRoleRepository(dataSource string, cacheConf cache.CacheConf) BackendRoleRepository {
	return &customBackendRoleRepository{
		cacheConf:  cacheConf,
		dataSource: dataSource,
	}
}

func (r *customBackendRoleRepository) Conn() error {
	if r.mysqlConn == nil {
		mysqlConn, err := modelx.NewMysqlConn(r.dataSource)
		if err != nil {
			return err
		}

		r.mysqlConn = mysqlConn
	}

	return nil
}

func (r *customBackendRoleRepository) FindOne(
	ctx context.Context,
	id int64,
	cols []string,
) (role *model.BackendRole, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	role, err = model.FindBackendRole(ctx, r.mysqlConn, uint64(id), cols...)
	return
}

func (r *customBackendRoleRepository) GetList(
	ctx context.Context,
	cols model.M,
	withCount int,
) (list []*model.BackendRole, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var selectCol string
	for k, v := range cols {
		switch k {
		case model.BackendRoleColumns.RoleID:
			if val, ok := v.(int64); ok && val > 0 {
				mods = append(mods, model.BackendRoleWhere.RoleID.EQ(uint64(val)))
			}
		case model.BackendRoleColumns.Name:
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, model.BackendRoleWhere.Name.EQ(val))
			}
		case model.BackendRoleColumns.Status:
			if val, ok := v.(int32); ok && val > 0 {
				mods = append(mods, model.BackendRoleWhere.Status.EQ(uint8(val-1)))
			}
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "ids":
			if val, ok := v.(string); ok && val != "" {
				ids := strings.Split(val, ",")
				if len(ids) > 0 {
					values := modelx.UniqueValues(ids, 64)
					mods = append(mods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.BackendRoleColumns.RoleID), values...))
				}
			}
		case "stati":
			if val, ok := v.(string); ok && val != "" {
				stati := strings.Split(val, ",")
				if len(stati) > 0 {
					values := modelx.UniqueValues(stati, 32)
					mods = append(mods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.BackendRoleColumns.Status), values...))
				}
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if withCount >= 0 {
		count, err = model.BackendRoles(mods...).Count(ctx, r.mysqlConn)
		if err != nil {
			return
		}

		if withCount == 0 {
			return
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.BackendRoleColumns.RoleID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.BackendRoles(mods...).All(ctx, r.mysqlConn)
	return
}

func (r *customBackendRoleRepository) Insert(ctx context.Context, role *model.BackendRole) (err error) {
	if err = r.Conn(); err != nil {
		return
	}

	err = role.Insert(ctx, r.mysqlConn, boil.Infer())
	return
}

func (r *customBackendRoleRepository) Update(ctx context.Context, role *model.BackendRole) (roleId int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	roleId, err = role.Update(ctx, r.mysqlConn, boil.Blacklist(
		model.BackendRoleColumns.CreatedBy,
		model.BackendRoleColumns.CreatedAt,
	))
	return
}

func (r *customBackendRoleRepository) UpdateBatch(ctx context.Context, ids []int64, cols model.M) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	if len(ids) > 0 {
		values := make([]interface{}, 0, len(ids))
		for _, id := range ids {
			values = append(values, uint64(id))
		}

		mods = append(mods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.BackendRoleColumns.RoleID), values...))
	}

	rowsAff, err = model.BackendRoles(mods...).UpdateAll(ctx, r.mysqlConn, cols)
	return
}

func (r *customBackendRoleRepository) ForceDeleteBatch(ctx context.Context, ids []int64) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	if len(ids) > 0 {
		values := make([]interface{}, 0, len(ids))
		for _, id := range ids {
			values = append(values, uint64(id))
		}

		mods = append(mods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.BackendRoleColumns.RoleID), values...))
	}

	rowsAff, err = model.BackendRoles(mods...).DeleteAll(ctx, r.mysqlConn)
	return
}

func (r *customBackendRoleRepository) SetMenus(
	ctx context.Context,
	role *model.BackendRole,
	menus []*model.BackendMenu,
) (err error) {
	if err = r.Conn(); err != nil {
		return
	}

	// err = role.SetMenuBackendMenus(ctx, r.mysqlConn, false, menus...)
	return
}

func (r *customBackendRoleRepository) SetPerms(
	ctx context.Context,
	role *model.BackendRole,
	perms []*model.BackendPermission,
) (err error) {
	if err = r.Conn(); err != nil {
		return
	}

	// err = role.SetPermissionBackendPermissions(ctx, r.mysqlConn, false, perms...)
	return
}
